package com.ymttest.utils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.experimental.categories.Category;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;

import com.ymatou.ResultParser;
import com.ymatou.database.Wapper.TestCaseWapper;
import com.ymatou.iapi.email.parameter.SendEmailBean;
import com.ymatou.iapi.email.service.SendEmailCall;
import com.ymt.core.tool.Logger;

/************************************************************************************
 * @File name :RunTests.java
 * @Author : zhouyi
 * @Date : 2015年9月1日
 * @Copyright : 洋码头
 ************************************************************************************/
class TestSuiteBean {

	//接口Ts_XX 类
	private Class<?> testSuiteClass;
	//接口内 Tc_xx 方法名称
	private String testCaseName;

	public Class<?> getTestSuiteClass() {
		return testSuiteClass;
	}

	public void setTestSuiteClass(Class<?> cls) {
		this.testSuiteClass = cls;
	}

	public String getTestCaseName() {
		return testCaseName;
	}

	public void setTestCaseName(String testCaseName) {
		this.testCaseName = testCaseName;
	}

}

public class RunTests {

	private static int threadNum = 1;

	private static ExecutorService SERVICE;

	private static List<Result> RESULTS = new ArrayList<Result>();
	// 结果文件路径
	private static String RESULT_PATH = new File("").getAbsolutePath()
			+ File.separator + "Results";

	/**
	 * @param runsuite
	 *            需要执行的case类列表
	 */
	public static List<Result> Run(List<TestSuiteBean> testCaseList,
			boolean isshuffle) {
		// 删除结果xml 文件
		deleteResultFile(new File(RESULT_PATH));
		// 打乱用例的执行的顺序，尽量避免同一个接口下多个用例同时并发执行
		if (isshuffle) {
			Collections.shuffle(testCaseList);
		}

		if (SERVICE == null || SERVICE.isShutdown()) {
			SERVICE = Executors.newFixedThreadPool(threadNum);
		}
		CountDownLatch countDownLatch = new CountDownLatch(testCaseList.size());
		for (TestSuiteBean testSuiteBean : testCaseList) {

			Class junitClass = testSuiteBean.getTestSuiteClass();
			String methodName = testSuiteBean.getTestCaseName();

			SERVICE.execute(() -> {

				Result r = RunCase(junitClass, methodName);
				//    @Ignore 的case 执行返回结果为null，排除掉
				if (r != null) {
					RESULTS.add(r);
				}
				countDownLatch.countDown();
			});
		}
		try {
			// 等待线程池里所有线程任务结束
			countDownLatch.await();
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info("运行error:" + e.getStackTrace());
		} finally {
			SERVICE.shutdown();
		}
		return RESULTS;
	}

	/**
	 * 单个用例执行方法
	 * 
	 * @param junitClass
	 *            执行的junit 类带包名 eg.com.ymatou.query.trading.api.testcase.
	 *            Ts_ExistsSellerOrderList
	 * @param testCaseMethod
	 *            junit 类里的用例方法名 eg.Tc_ExistsSellerOrderList_001
	 */
	public static Result RunCase(Class junitClass, String testCaseMethod) {
		Result result = new Result();
		JUnitCore junitRunner = new JUnitCore();
		Request request = Request.method(junitClass, testCaseMethod);
		result = junitRunner.run(request);
		Logger.info("Run " + testCaseMethod + " Duration : "
				+ result.getRunTime() + "ms");
		return result;
	}

	/**
	 * 获取一个域名下，所有junit类文件所有用例方法  list
	 * 
	 * @param domain
	 *            站点域名 eg. operate.trading.iapi.ymatou.com
	 * @param included
	 *            需要执行的tag 不需要时传入ALL
	 * @return 站点的所有 类跟方法映射 eg. Ts->Tc1,Ts->Tc2,...
	 */
	public static List<TestSuiteBean> getTestMethods(String domain,
			String included) {

		List<TestSuiteBean> junitCaseList = new java.util.LinkedList<TestSuiteBean>();
		List<String> fileList = new ArrayList<String>();

		String path = "";
		String[] tempp = null;
		tempp = domain.split("\\.");
		for (String tp : tempp) {
			path = tp + File.separator + path;
		}
		// path 为 domain 反过来 加testcase目录
		path = System.getProperty("user.dir") + File.separator + "bin"
				+ File.separator + path.substring(0, path.length())
				+ "testcase";
		Logger.info("run case path:"+path);
		// 该目录下所有文件路径
		fileList = TestDataManager.getFilelist(path);
		//过滤old的情况
		for (String fname : fileList) {
			if(fname.contains("testcase\\old")){
				continue;
			}
			// 执行case的TestClass
			Class<?> cls = null;
			try {
				cls = TestDataManager.getClassNameByFileName(fname);
			} catch (Exception e) {
				Logger.debug(fname + ":不是一个java class,不处理！");
				e.printStackTrace();
			}
			if (cls != null) {
				//Logger.info("cls class is:"+cls);
				// 获取当前junit 类下所有用例方法
				Method[] ms = cls.getDeclaredMethods();
				for (Method m1 : ms) {
					if (m1.getName().startsWith("Tc_")) {
						Logger.info(fname+":m1 method is:" + m1.getName());
						String categoryFlag ="";
						if( m1.getAnnotation(Category.class)!=null){
							categoryFlag = m1.getAnnotation(Category.class)
								.toString();
						}
						TestSuiteBean testSuiteBean = new TestSuiteBean();

						// 默认跑所有级别case
						if (included.equals("ALL")
								|| categoryFlag.contains(included)) {

							testSuiteBean.setTestSuiteClass(cls);
							testSuiteBean.setTestCaseName(m1.getName());
							junitCaseList.add(testSuiteBean);

						}
					}
				}
			}
		}
		return junitCaseList;
	}

	/**
	 * 初始化环境
	 * 
	 * @param env
	 */
	public static void initProp(String env) {
		EnvSetup.setEnv(env.toLowerCase());
		//通过jenkins 跑case ，强制设置控制台不输出case 运行日志
		Logger.setDebug(false);

		Logger.info("jenkins 运行日志开启状态为：" + Logger.isDebug());
		String dns = EnvSetup.getSystemUrl("dns");
		if (TestDataManager.isWindowsOS()) {
			if (dns != null && !dns.equals("null") && dns.length() > 0) {
				TestDataManager.ChangeDns("本地连接", dns);
			}
		}
	}

	/**
	 * 返回所有执行失败用例的Map list class->case1,...
	 * 
	 * @param resultList
	 */
	private static List<TestSuiteBean> getFailedCaseList(List<Result> resultList) {
		// 所有失败待重run的case
		List<TestSuiteBean> failedCaseList = new java.util.LinkedList<TestSuiteBean>();
		// failedtestCaseMap
		Set<String> failedSet = new HashSet<String>();
		resultList.forEach(result -> {
			result.getFailures().forEach(failure -> {
				String errtest = failure.getTestHeader();

				// junit 统计失败的case会算上error的情况。case如果有异常报错+断言失败，junit 会认为case失败2次
				// 去重
					if (errtest.startsWith("Tc_")) {
						failedSet.add(errtest);
					}
				});
		});

		// 将失败的case加到failedCaseList里
		failedSet.forEach(failedCase -> {
			Logger.debug("failedCase：" + failedCase);
			String cls = TestDataManager.getRegexList(failedCase,
					"(?<=\\().*?(?=\\))").get(0);
			String methodName = TestDataManager.getRegexList(failedCase,
					"^.*?(?=\\()").get(0);
			Class<?> JunitClass = null;
			try {
				JunitClass = (Class<?>) Class.forName(cls);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			TestSuiteBean testSuiteBean = new TestSuiteBean();
			testSuiteBean.setTestSuiteClass(JunitClass);
			testSuiteBean.setTestCaseName(methodName);

			failedCaseList.add(testSuiteBean);
		});
		return failedCaseList;
	}

	/**
	 * 删除结果文件夹及xml 结果文件
	 * 
	 * @param File
	 */
	private static void deleteResultFile(File file) {
		if (file.isDirectory()) {
			File[] temp = file.listFiles();
			for (File f : temp) {
				deleteResultFile(f);
			}
		}
		file.delete();
	}

	public static void main(String args[]) {

//		System.out.println("args[0]:"+args[0]);
//		System.out.println("args[1]:"+args[1]);
//		System.out.println("args[2]:"+args[2]); 
//		System.out.println("args[3]:"+args[3]);
//		System.out.println("args[4]:"+args[4]);

		long before = System.currentTimeMillis();

		if (args.length < 2) {
			try {
				throw new Exception("至少需要2个参数:env runcase");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//运行的环境
		String env = args[0];
		//uat环境已经没有了 出现uat依然跑sit
		if(env.startsWith("UAT")){
			env="SIT1";
		}
		//指定运行站点域名  api.trading.operate.ymatou.com
		String runcase = args[1];
		try {
			String thread = args[4];
			threadNum = Integer.valueOf(thread);
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "threadNum 配置项必须为数字");
			Logger.info("threadNum 默认为1个线程");
		}
		Logger.info("threadNum:" + threadNum);
		// 默认跑所有级别用例 ALL  P1,;P2;P3
		String runIncluded = "ALL";
		try {
			runIncluded = args[3].toUpperCase();
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  runIncluded 未配置");
			Logger.info("runIncluded 默认为ALL,跑所有case");
		}
		//运行case 级别
		Logger.info("runIncluded:" + runIncluded);
		Logger.info("runcase:" + runcase);

		// 默认失败尝试重跑一次
		int retryTime = 1;
		try {
			retryTime = Integer.valueOf(args[4]);
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  retryTime 未配置");
			Logger.info("retryTime 默认为1,失败尝试重跑1次");
		}
		boolean isshuffle = false;
		try {
			isshuffle = Boolean.valueOf(args[5]);
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  isshuffle 未配置");
		}
		Logger.info("isshuffle:" + isshuffle);
		initProp(env);
		// 获取指定域名下所有用例

		List<TestSuiteBean> junitTestCaseList = RunTests.getTestMethods(
				runcase, runIncluded);

		Logger.info("需要执行的case 总数为" + junitTestCaseList.size());
		int allsize=junitTestCaseList.size();
		if (junitTestCaseList.size() < 1)
			System.exit(0);

		// 所有用例运行结果List
		List<Result> resultList = RunTests.Run(junitTestCaseList, isshuffle);

		long after = System.currentTimeMillis();

		int runCount = (int) resultList.stream()
				.mapToInt(result -> result.getRunCount()).summaryStatistics()
				.getSum();

		/*			int failureCount = (int) resultList.stream()
							.mapToInt(result -> result.getFailureCount())
							.summaryStatistics().getSum();*/
		int ignoreCount = (int) resultList.stream()
				.mapToInt(result -> result.getIgnoreCount())
				.summaryStatistics().getSum();

		//获取执行失败的case 列表
		List<TestSuiteBean> failedCaseList = getFailedCaseList(resultList);

		Logger.info("第一次运行case结束============== ");
		Logger.info("Actual RunCount:" + runCount);
		Logger.info("PassedCount:" + (runCount - failedCaseList.size()));
		Logger.info("FailureCount:" + failedCaseList.size());
		Logger.info("IgnoreCount:" + ignoreCount);
		Logger.info("RunTime:" + (after - before) / 1000 + "s");

		Logger.info("开始解析结果文件入库============== ");

		ResultParser resultParser = new ResultParser();
		TestCaseWapper tcw = new TestCaseWapper();

		// 设置结果文件路径
		resultParser.setPath(RESULT_PATH);

		//执行的批次号
		int passid = 0;
		try {
			passid = resultParser.parserXmlToDb(tcw, env, runcase);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			Logger.info("首次解析结果文件入库失败!" + e.getMessage());
			e.printStackTrace();
		}

		Logger.info("解析结果文件入库结束============== ");

		//没有失败的退出
		if (failedCaseList.size() < 1)
			System.exit(0);

		// 首次执行失败的case map list 集合
		List<Result> resultFailedList = new ArrayList<Result>();

		//错误case 重跑只启用一个线程
		SERVICE.shutdown();

		threadNum = 1;

		int count = 1;
		while (retryTime > 0) {
			Logger.info("开始第" + count + "次重跑失败的case============== ");

			resultFailedList = RunTests.Run(failedCaseList, isshuffle);
			// 解析失败重跑用例的结果文件入库
			try {
				resultParser.parserTestContentsToDb(tcw, passid);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				Logger.info("解析失败重跑用例的结果文件入库失败!" + e.getMessage());
				e.printStackTrace();
			}
			// 递归运行失败的测试用例
			failedCaseList = getFailedCaseList(resultFailedList);
			count++;
			retryTime--;
		}
		Logger.info("运行case结束============== ");

		if (!SERVICE.isShutdown()) {
			SERVICE.shutdown();
		}
		
//		//通过率过低的情况下返回失败
		Double faildrate=Double.valueOf(failedCaseList.size())/Double.valueOf(allsize);
		System.out.println("failedCaseList.size():"+failedCaseList.size());
		System.out.println("allsize:"+allsize);
		//Double faildrate=0.3;
		if(faildrate>0.2 && args[args.length-1].contains("@")){
	     try {
			String toEmail=args[args.length-1];
			SendEmailBean sendemailBean=new SendEmailBean();
			SendEmailCall sendemailCall=new SendEmailCall();
			
			sendemailBean.setAppId("AutoTest");
			sendemailBean.setContent("站点："+args[1]+" 自动化case失败率高于20%,请及时修改！");
			// 测试账号 配置在mail_sender 表中
			sendemailBean.setFromMail("alarm@ymatou.com");
			sendemailBean.setFromName("自动化测试邮件AutoTest");
			sendemailBean.setSubject("自动化测试邮件AutoTest");
			sendemailBean.setToMail(toEmail);
			sendemailCall.setData(sendemailBean);
			sendemailCall.callService();
			
			System.out.println("失败率高于20%,触发异常退出");
			System.exit(-1);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				System.exit(-1);
			}
		}
		System.exit(0);
	}
}
